package club.kynb.mall.application.service;

import club.kynb.mall.application.assembler.MallCommonAssembler;
import club.kynb.mall.application.constant.LockKeyConst;
import club.kynb.mall.application.context.MallUserContext;
import club.kynb.mall.application.convert.MallCommonConvertor;
import club.kynb.mall.application.model.model.command.ShoppingCartAddCommand;
import club.kynb.mall.application.model.model.command.ShoppingCartCheckedCommand;
import club.kynb.mall.application.model.model.command.ShoppingCartReduceCommand;
import club.kynb.mall.application.model.model.dto.UserShoppingCartDTO;
import club.kynb.mall.order.api.IShoppingCartService;
import club.kynb.mall.order.model.dto.ShoppingCartDTO;
import club.kynb.mall.product.api.IProductInfoService;
import club.kynb.mall.product.dto.ProductByParamDTO;
import club.kynb.mall.product.dto.ProductSpuDetailDTO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.pizza.common.web.exception.Errors;
import org.pizza.common.web.response.CommonCode;
import org.pizza.util.Checker;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author kynb_club@163.com
 * @since 2021/6/30 10:15 下午
 */
@Slf4j
@Service
@AllArgsConstructor
public class ShoppingCartApplicationService {

    private final IShoppingCartService iShoppingCartService;
    private final IProductInfoService iProductInfoService;
    private final RedissonClient redissonClient;


    public UserShoppingCartDTO userShoppingCartList() {
        final MallUserContext mallUserContext = MallUserContext.get(true);
        final Long userId = mallUserContext.getUserDTO().getId();
        final List<ShoppingCartDTO> dtoList = iShoppingCartService.userShoppingCart(userId);
        final List<Long> spuIdList = dtoList.stream()
                .map(shoppingCartDTO -> Long.valueOf(shoppingCartDTO.getSpuId()))
                .collect(Collectors.toList());
        final List<ProductSpuDetailDTO> productSpuList = iProductInfoService.listOnSaleByParam(new ProductByParamDTO().setIds(spuIdList));
        return MallCommonAssembler.assembleUserShoppingCartDTO(dtoList,productSpuList);
    }

    public UserShoppingCartDTO add(ShoppingCartAddCommand command) {
        final MallUserContext mallUserContext = MallUserContext.get(true);
        final Long userId = mallUserContext.getUserDTO().getId();
        //检验库存和传入SPU、SKU的ID是否正确
        iProductInfoService.check(command.getSpuId(), command.getSkuId());
        boolean isLocked = false;
        String key = String.format("%s:%s", LockKeyConst.ADD_SHOPPING_CART, userId);
        final RLock lock = redissonClient.getLock(key);
        try {
            Checker.ifNotThrow(isLocked = lock.tryLock(), () -> Errors.BIZ.exception(CommonCode.BUSY));
            final ShoppingCartDTO shoppingCartDTO = MallCommonConvertor.covert(command.setUserId(userId));
            iShoppingCartService.add(shoppingCartDTO);
        } finally {
            if (isLocked) {
                lock.unlock();
            }
        }
        return userShoppingCartList();

    }

    public UserShoppingCartDTO reduce(ShoppingCartReduceCommand command) {
        final MallUserContext mallUserContext = MallUserContext.get(true);
        final Long userId = mallUserContext.getUserDTO().getId();
        boolean isLocked = false;
        String key = String.format("%s:%s", LockKeyConst.ADD_SHOPPING_CART, userId);
        final RLock lock = redissonClient.getLock(key);
        try {
            Checker.ifNotThrow(isLocked = lock.tryLock(), () -> Errors.BIZ.exception(CommonCode.BUSY));
            iShoppingCartService.reduce(userId, command.getSpuId(), command.getSkuId(), command.getNum());
        } finally {
            if (isLocked) {
                lock.unlock();
            }
        }
        return userShoppingCartList();
    }

    public UserShoppingCartDTO checked(ShoppingCartCheckedCommand command) {
        final MallUserContext mallUserContext = MallUserContext.get(true);
        final Long userId = mallUserContext.getUserDTO().getId();
        command.setUserId(userId);
        boolean isLocked = false;
        String key = String.format("%s:%s", LockKeyConst.ADD_SHOPPING_CART, userId);
        final RLock lock = redissonClient.getLock(key);
        try {
            Checker.ifNotThrow(isLocked = lock.tryLock(), () -> Errors.BIZ.exception(CommonCode.BUSY));
            iShoppingCartService.checked(command.getUserId(),command.getCheckedSkuIdList(),command.getUnCheckedSkuIdList());
        } finally {
            if (isLocked) {
                lock.unlock();
            }
        }
        return userShoppingCartList();
    }

    public Long userShoppingCartCount() {
        final MallUserContext mallUserContext = MallUserContext.get(true);
        final Long userId = mallUserContext.getUserDTO().getId();
        final List<ShoppingCartDTO> dtoList = iShoppingCartService.userShoppingCart(userId);
        return dtoList.stream().mapToLong(ShoppingCartDTO::getSkuNum).sum();
    }
}
